{
  "cells": [
    {
      "attachments": {},
      "cell_type": "markdown",
      "metadata": {
        "id": "UvkfB7BxSuaB"
      },
      "source": [
        "# Graph Attention Networks"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 41,
      "metadata": {},
      "outputs": [],
      "source": [
        "import torch\n",
        "!pip install -q torch-scatter~=2.1.0 torch-sparse~=0.6.16 torch-cluster~=1.6.0 torch-spline-conv~=1.2.1 torch-geometric==2.2.0 -f https://data.pyg.org/whl/torch-{torch.__version__}.html\n",
        "\n",
        "torch.manual_seed(1)\n",
        "torch.cuda.manual_seed(1)\n",
        "torch.cuda.manual_seed_all(1)\n",
        "torch.backends.cudnn.deterministic = True\n",
        "torch.backends.cudnn.benchmark = False"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 42,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "uWA4OPhqiCOb",
        "outputId": "f5a1ee9d-4dfe-49e5-8f4d-10359d62e931"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "array([[1, 1, 1, 1],\n",
              "       [1, 1, 0, 0],\n",
              "       [1, 0, 1, 1],\n",
              "       [1, 0, 1, 1]])"
            ]
          },
          "execution_count": 42,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "import numpy as np\n",
        "np.random.seed(0)\n",
        "\n",
        "A = np.array([\n",
        "    [1, 1, 1, 1],\n",
        "    [1, 1, 0, 0],\n",
        "    [1, 0, 1, 1],\n",
        "    [1, 0, 1, 1]\n",
        "])\n",
        "A"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 43,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "nJ4wYhV3ibwY",
        "outputId": "ce79d4ac-f07c-410e-9c27-181cfa00a267"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "array([[ 0.09762701,  0.43037873,  0.20552675,  0.08976637],\n",
              "       [-0.1526904 ,  0.29178823, -0.12482558,  0.783546  ],\n",
              "       [ 0.92732552, -0.23311696,  0.58345008,  0.05778984],\n",
              "       [ 0.13608912,  0.85119328, -0.85792788, -0.8257414 ]])"
            ]
          },
          "execution_count": 43,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "X = np.random.uniform(-1, 1, (4, 4))\n",
        "X"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 44,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "8ikFGBdJiRph",
        "outputId": "992a6cd0-fdbe-41c6-9d1b-9589bfe2dc0e"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "array([[-0.95956321,  0.66523969,  0.5563135 ,  0.7400243 ],\n",
              "       [ 0.95723668,  0.59831713, -0.07704128,  0.56105835]])"
            ]
          },
          "execution_count": 44,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "W = np.random.uniform(-1, 1, (2, 4))\n",
        "W"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 45,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "UPov5b8Cisxx",
        "outputId": "cc5766d0-063a-408d-a297-c27aa14cac72"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "array([[-0.76345115,  0.27984204, -0.71329343,  0.88933783]])"
            ]
          },
          "execution_count": 45,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "W_att = np.random.uniform(-1, 1, (1, 4))\n",
        "W_att"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 46,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "PBglLK7OjPs6",
        "outputId": "0e38b24f-503b-4e80-9251-38bae4a3ef56"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "(array([0, 0, 0, 0, 1, 1, 2, 2, 2, 3, 3, 3], dtype=int64),\n",
              " array([0, 1, 2, 3, 0, 1, 0, 2, 3, 0, 2, 3], dtype=int64))"
            ]
          },
          "execution_count": 46,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "connections = np.where(A > 0)\n",
        "connections"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 47,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "Dr_hpZKK_v0Z",
        "outputId": "540b7ec3-b6b5-4703-fb22-26d19f4282ba"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "array([[ 0.37339233,  0.38548525,  0.37339233,  0.38548525],\n",
              "       [ 0.37339233,  0.38548525,  0.85102612,  0.47765279],\n",
              "       [ 0.37339233,  0.38548525, -0.67755906,  0.73566587],\n",
              "       [ 0.37339233,  0.38548525, -0.65268413,  0.24235977],\n",
              "       [ 0.85102612,  0.47765279,  0.37339233,  0.38548525],\n",
              "       [ 0.85102612,  0.47765279,  0.85102612,  0.47765279],\n",
              "       [-0.67755906,  0.73566587,  0.37339233,  0.38548525],\n",
              "       [-0.67755906,  0.73566587, -0.67755906,  0.73566587],\n",
              "       [-0.67755906,  0.73566587, -0.65268413,  0.24235977],\n",
              "       [-0.65268413,  0.24235977,  0.37339233,  0.38548525],\n",
              "       [-0.65268413,  0.24235977, -0.67755906,  0.73566587],\n",
              "       [-0.65268413,  0.24235977, -0.65268413,  0.24235977]])"
            ]
          },
          "execution_count": 47,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "np.concatenate([(X @ W.T)[connections[0]], (X @ W.T)[connections[1]]], axis=1)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 48,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "1JqJVr1uibY5",
        "outputId": "85874ff9-ab68-4d3b-89cd-dc1e6ec28e8a"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "array([[-0.1007035 , -0.35942847,  0.96036209,  0.50390318, -0.43956122,\n",
              "        -0.69828618,  0.79964181,  1.8607074 ,  1.40424849,  0.64260322,\n",
              "         1.70366881,  1.2472099 ]])"
            ]
          },
          "execution_count": 48,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "a = W_att @ np.concatenate([(X @ W.T)[connections[0]], (X @ W.T)[connections[1]]], axis=1).T\n",
        "a"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 49,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "sdfcccfElpW1",
        "outputId": "556496ea-c83b-4630-b59b-c4fff3525bf8"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "array([[-0.0201407 , -0.07188569,  0.96036209,  0.50390318, -0.08791224,\n",
              "        -0.13965724,  0.79964181,  1.8607074 ,  1.40424849,  0.64260322,\n",
              "         1.70366881,  1.2472099 ]])"
            ]
          },
          "execution_count": 49,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "def leaky_relu(x, alpha=0.2):\n",
        "    return np.maximum(alpha*x, x)\n",
        "\n",
        "e = leaky_relu(a)\n",
        "e"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 50,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "HRhBAlqgl4Lg",
        "outputId": "b23c654f-388b-47ac-f8a4-89968d10ada8"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "array([[-0.0201407 , -0.07188569,  0.96036209,  0.50390318],\n",
              "       [-0.08791224, -0.13965724,  0.        ,  0.        ],\n",
              "       [ 0.79964181,  0.        ,  1.8607074 ,  1.40424849],\n",
              "       [ 0.64260322,  0.        ,  1.70366881,  1.2472099 ]])"
            ]
          },
          "execution_count": 50,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "E = np.zeros(A.shape)\n",
        "E[connections[0], connections[1]] = e[0]\n",
        "E"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 51,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "A_4cc3Mal_bZ",
        "outputId": "077350d5-9773-44ee-c011-31f241f90197"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "array([[0.15862414, 0.15062488, 0.42285965, 0.26789133],\n",
              "       [0.24193418, 0.22973368, 0.26416607, 0.26416607],\n",
              "       [0.16208847, 0.07285714, 0.46834625, 0.29670814],\n",
              "       [0.16010498, 0.08420266, 0.46261506, 0.2930773 ]])"
            ]
          },
          "execution_count": 51,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "def softmax2D(x, axis):\n",
        "    e = np.exp(x - np.expand_dims(np.max(x, axis=axis), axis))\n",
        "    sum = np.expand_dims(np.sum(e, axis=axis), axis)\n",
        "    return e / sum\n",
        "\n",
        "W_alpha = softmax2D(E, 1)\n",
        "W_alpha"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 52,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "QeADRiD7mVYc",
        "outputId": "6495dab3-c582-4658-e750-0c4700feddf8"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "array([[-1.10126376,  1.99749693],\n",
              "       [-0.33950544,  0.97045933],\n",
              "       [-1.03570438,  1.53614075],\n",
              "       [-1.03570438,  1.53614075]])"
            ]
          },
          "execution_count": 52,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "H = A.T @ W_alpha @ X @ W.T\n",
        "H"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 53,
      "metadata": {
        "id": "LHWS5blDTCVv"
      },
      "outputs": [],
      "source": [
        "from torch_geometric.datasets import Planetoid\n",
        "\n",
        "# Import dataset from PyTorch Geometric\n",
        "dataset = Planetoid(root=\".\", name=\"Cora\")\n",
        "data = dataset[0]"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 54,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "45iaJfMLTJKX",
        "outputId": "37ee3c34-bd0a-4344-841b-884766b38712"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "GAT(\n",
            "  (gat1): GATv2Conv(1433, 32, heads=8)\n",
            "  (gat2): GATv2Conv(256, 7, heads=1)\n",
            ")\n",
            "Epoch   0 | Train Loss: 1.978 | Train Acc: 12.86% | Val Loss: 1.94 | Val Acc: 13.80%\n",
            "Epoch  20 | Train Loss: 0.238 | Train Acc: 96.43% | Val Loss: 1.04 | Val Acc: 67.40%\n",
            "Epoch  40 | Train Loss: 0.165 | Train Acc: 98.57% | Val Loss: 0.95 | Val Acc: 71.00%\n",
            "Epoch  60 | Train Loss: 0.209 | Train Acc: 96.43% | Val Loss: 0.91 | Val Acc: 71.80%\n",
            "Epoch  80 | Train Loss: 0.172 | Train Acc: 100.00% | Val Loss: 0.93 | Val Acc: 70.80%\n",
            "Epoch 100 | Train Loss: 0.190 | Train Acc: 97.86% | Val Loss: 0.96 | Val Acc: 70.80%\n",
            "GAT test accuracy: 81.10%\n"
          ]
        }
      ],
      "source": [
        "import torch\n",
        "torch.manual_seed(1)\n",
        "import torch.nn.functional as F\n",
        "from torch_geometric.nn import GATv2Conv, GCNConv\n",
        "from torch.nn import Linear, Dropout\n",
        "\n",
        "\n",
        "def accuracy(y_pred, y_true):\n",
        "    \"\"\"Calculate accuracy.\"\"\"\n",
        "    return torch.sum(y_pred == y_true) / len(y_true)\n",
        "\n",
        "\n",
        "class GAT(torch.nn.Module):\n",
        "    def __init__(self, dim_in, dim_h, dim_out, heads=8):\n",
        "        super().__init__()\n",
        "        self.gat1 = GATv2Conv(dim_in, dim_h, heads=heads)\n",
        "        self.gat2 = GATv2Conv(dim_h*heads, dim_out, heads=1)\n",
        "\n",
        "    def forward(self, x, edge_index):\n",
        "        h = F.dropout(x, p=0.6, training=self.training)\n",
        "        h = self.gat1(h, edge_index)\n",
        "        h = F.elu(h)\n",
        "        h = F.dropout(h, p=0.6, training=self.training)\n",
        "        h = self.gat2(h, edge_index)\n",
        "        return F.log_softmax(h, dim=1)\n",
        "\n",
        "    def fit(self, data, epochs):\n",
        "        criterion = torch.nn.CrossEntropyLoss()\n",
        "        optimizer = torch.optim.Adam(self.parameters(), lr=0.01, weight_decay=0.01)\n",
        "\n",
        "        self.train()\n",
        "        for epoch in range(epochs+1):\n",
        "            optimizer.zero_grad()\n",
        "            out = self(data.x, data.edge_index)\n",
        "            loss = criterion(out[data.train_mask], data.y[data.train_mask])\n",
        "            acc = accuracy(out[data.train_mask].argmax(dim=1), data.y[data.train_mask])\n",
        "            loss.backward()\n",
        "            optimizer.step()\n",
        "\n",
        "            if(epoch % 20 == 0):\n",
        "                val_loss = criterion(out[data.val_mask], data.y[data.val_mask])\n",
        "                val_acc = accuracy(out[data.val_mask].argmax(dim=1), data.y[data.val_mask])\n",
        "                print(f'Epoch {epoch:>3} | Train Loss: {loss:.3f} | Train Acc: {acc*100:>5.2f}% | Val Loss: {val_loss:.2f} | Val Acc: {val_acc*100:.2f}%')\n",
        "\n",
        "    @torch.no_grad()\n",
        "    def test(self, data):\n",
        "        self.eval()\n",
        "        out = self(data.x, data.edge_index)\n",
        "        acc = accuracy(out.argmax(dim=1)[data.test_mask], data.y[data.test_mask])\n",
        "        return acc\n",
        "\n",
        "# Create the Vanilla GNN model\n",
        "gat = GAT(dataset.num_features, 32, dataset.num_classes)\n",
        "print(gat)\n",
        "\n",
        "# Train\n",
        "gat.fit(data, epochs=100)\n",
        "\n",
        "# Test\n",
        "acc = gat.test(data)\n",
        "print(f'GAT test accuracy: {acc*100:.2f}%')"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 55,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "nDT78dtSLCb2",
        "outputId": "e31a7490-cd52-406b-a901-fe51e27cb5a0"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "Data(x=[3327, 3703], edge_index=[2, 9104], y=[3327], train_mask=[3327], val_mask=[3327], test_mask=[3327])"
            ]
          },
          "execution_count": 55,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "dataset = Planetoid(root=\".\", name=\"CiteSeer\")\n",
        "data = dataset[0]\n",
        "data"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 56,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 1000
        },
        "id": "4n1Ke07DQ1RZ",
        "outputId": "5aee6584-4ce4-42fe-e6f1-277a2e3bdb96"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<BarContainer object of 32 artists>"
            ]
          },
          "execution_count": 56,
          "metadata": {},
          "output_type": "execute_result"
        },
        {
          "data": {
            "image/png": "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/rxef/11ffrpp+rXr59atWrl0L9gwQKXFQcAANAYnA5E+/btU9++fSVJBw8edOizWCyuqQoAAKAROR2INm3a5I46AAAAPOaiH7v/+9//ro8//lhnzpyRJBmG4bKiAAAAGpPTgehf//qXBg8erGuuuUa33XabfvjhB0lScnKyHnvsMZcXCAAA4G5OB6IpU6bIx8dHR48eVcuWLe3td999t7KyslxaHAAAQGNw+h6iTz75RB9//LGuvPJKh/arr75aR44ccVlhAAAAjcXpM0SVlZUOZ4YuKCsrk5+fn0uKAgAAaExOB6IbbrhB//d//2dft1gsqq2tVWZmpm6++WaXFgcAANAYnL5klpmZqcGDB2vnzp06d+6cnnjiCe3fv19lZWXatm2bO2oEAABwK6fPEPXo0UMHDx7UoEGDdOedd6qyslKjRo3S7t271blzZ3fUCAAA4FZOnyGSpKCgID355JOurgUAAMAjLioQnThxQq+99pqKiookSdHR0XrggQfUrl07lxYHAADQGJy+ZLZlyxZFRkZq0aJFOnHihE6cOKFFixYpKipKW7ZscXmB33//ve699161b99eAQEB6tmzp3bu3GnvNwxDM2fOVIcOHRQQEKD4+HgdOnTIYRtlZWVKTExUYGCggoODlZycrFOnTrm8VgAA0Dw5HYhSUlJ099136/Dhw1qzZo3WrFmjb775RgkJCUpJSXFpcSdOnNDAgQPl4+OjDRs26MCBA3ruuefUtm1b+5jMzEwtWrRIy5YtU15enlq1aqWhQ4fq7Nmz9jGJiYnav3+/srOztX79em3ZskUTJkxwaa0AAKD5shhO/ghZQECACgsL1aVLF4f24uJi9enTx/7bZq6Qnp6ubdu26fPPP6+33zAMhYeH67HHHtPjjz8uSaqoqFBoaKhWrFihhIQEFRUVKTo6Wvn5+YqNjZUkZWVl6bbbbtN3332n8PDw/1iHzWZTUFCQKioqFBgY6LLj+3ci0z90WP923oh62wAAwL/nzPe302eI+vbta7936KeKiorUu3dvZzf3iz744APFxsbqrrvuUkhIiGJiYvTqq6/a+w8fPiyr1ar4+Hh7W1BQkAYMGKDc3FxJUm5uroKDg+1hSJLi4+Pl5eWlvLy8evdbVVUlm83msAAAgEtXg26q3rNnj/3viRMnatKkSfr73/+u6667TpK0fft2LVmy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//77dezYMW3atEl/+9vf9NJLL6m0tNRhO3fddZdKS0u1YcMGFRQUqG/fvho8eLDKysrq3e+pU6d0++23Kzo6WgUFBZo9e7Yef/xxhzHl5eW65ZZbFBMTo507dyorK0slJSUaM2aMfUxaWpq2bdumDz74QNnZ2fr888+1a9euOvt79tln1bt3b+3evVszZszQ119/rWHDhmn06NHas2eP3nnnHW3dulWpqan2z6Smpio3N1erVq3Snj17dNddd2nYsGE6dOjQf5x7AE4yAMANkpKSjDvvvNMwDMO47rrrjAcffNAwDMNYu3at8dP/esaOHWvceuutDp+dOnWqER0dbRiGYRQXFxuSjB07dtj7i4qKDEnG888/bxiGYXz++edGYGCgcfbsWYftdO7c2Xj55Zfrre/ll1822rdvb5w5c8betnTpUkOSsXv3bsMwDGPu3LnGkCFDHD537NgxQ5JRXFxs2Gw2w8fHx1i9erW9v7y83GjZsqUxadIke1unTp2MkSNHOmwnOTnZmDBhgkPb559/bnh5eRlnzpwxjhw5YrRo0cL4/vvvHcYMHjzYmD59er3HBODieXs0jQEwhfnz5+uWW26pcwZGkoqKinTnnXc6tA0cOFALFy5UTU2NioqK5O3trX79+tn7u3btquDgYPv6l19+qVOnTql9+/YO2zlz5oy+/vrremsqKipSr1695O/vb2+Li4tzGPPll19q06ZNat26dZ3Pf/311zpz5oyqq6vVv39/e3tQUJC6dOlSZ3xsbGydbe/Zs8fh8p9hGKqtrdXhw4f1zTffqKamRtdcc43D56qqquocJ4Bfj0AEwO1uvPFGDR06VNOnT9f999/v8u2fOnVKHTp0qPf+mp8Gp4vZ7h133KH58+fX6evQoYP+/ve/N3hbrVq1qrPthx9+WBMnTqwztmPHjtqzZ49atGihgoICtWjRwqG/voAG4NchEAFoFPPmzVOfPn3qnD3p1q2btm3b5tC2bds2XXPNNWrRooW6du2q8+fPq6CgQNdee60kqbi4WOXl5fbxffv2ldVqlbe3tyIjIxtUT7du3fSXv/xFZ8+etZ8l2r59u8OYvn376t1331VkZKS8vev+d/mb3/xGPj4+ys/PV8eOHSVJFRUVOnjwoG688cZf3H/fvn114MABXXXVVfX2x8TEqKamRqWlpbrhhhsadEwALh43VQNoFD179lRiYqIWLVrk0P7YY48pJydHc+fO1cGDB/XGG2/oxRdftF9e69Kli4YNG6aHH35YeXl5Kigo0EMPPaSAgAD7NuLj4xUXF6eRI0fqk08+0bfffqsvvvhCTz75pHbu3FlvPWPHjpXFYtH48eN14MABffTRR3r22WcdxqSkpKisrEz33HOP8vPz9fXXX+vjjz/WAw88oJqaGrVp00ZJSUmaOnWqNm3apP379ys5OVleXl7/8V1L06ZN0xdffKHU1FQVFhbq0KFDev/99+03VV9zzTVKTEzUuHHjtGbNGh0+fFg7duxQRkaGPvzwQ6fnH8AvIxABaDRz5sxRbW2tQ1vfvn3117/+VatWrVKPHj00c+ZMzZkzx+HS2vLlyxUeHq7f/va3GjVqlCZMmKCQkBB7v8Vi0UcffaQbb7xRDzzwgK655holJCToyJEjCg0NrbeW1q1ba926ddq7d69iYmL05JNP1rk0Fh4erm3btqmmpkZDhgxRz549NXnyZAUHB8vL68f/PhcsWKC4uDjdfvvtio+P18CBA9WtWzeHe5Pq06tXL23evFkHDx7UDTfcoJiYGM2cOVPh4eEOxz1u3Dg99thj6tKli0aOHOlwNgqA61gMwzA8XQQAXCoqKyt1xRVX6LnnnlNycrKnywHQQNxDBAC/wu7du/XVV1+pf//+qqio0Jw5cySpzpNzAJo2AhEA/ErPPvusiouL5evrq379+unzzz/XZZdd5umyADiBS2YAAMD0uKkaAACYHoEIAACYHoEIAACYHoEIAACYHoEIAACYHoEIAACYHoEIAACYHoEIAACY3v8DulfUJhjqyKMAAAAASUVORK5CYII=",
            "text/plain": [
              "<Figure size 640x480 with 1 Axes>"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "import matplotlib.pyplot as plt\n",
        "from torch_geometric.utils import degree\n",
        "from collections import Counter\n",
        "\n",
        "# Get list of degrees for each node\n",
        "degrees = degree(dataset[0].edge_index[0]).numpy()\n",
        "\n",
        "# Count the number of nodes for each degree\n",
        "numbers = Counter(degrees)\n",
        "\n",
        "# Bar plot\n",
        "fig, ax = plt.subplots()\n",
        "ax.set_xlabel('Node degree')\n",
        "ax.set_ylabel('Number of nodes')\n",
        "plt.bar(numbers.keys(), numbers.values())"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 57,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "rW0bm5aoL_uf",
        "outputId": "b362b613-38dd-4e29-f40d-85a9bc3d742a"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "GAT(\n",
            "  (gat1): GATv2Conv(3703, 16, heads=8)\n",
            "  (gat2): GATv2Conv(128, 6, heads=1)\n",
            ")\n",
            "Epoch   0 | Train Loss: 1.815 | Train Acc: 15.00% | Val Loss: 1.81 | Val Acc: 14.20%\n",
            "Epoch  20 | Train Loss: 0.173 | Train Acc: 99.17% | Val Loss: 1.15 | Val Acc: 63.80%\n",
            "Epoch  40 | Train Loss: 0.113 | Train Acc: 99.17% | Val Loss: 1.12 | Val Acc: 64.80%\n",
            "Epoch  60 | Train Loss: 0.099 | Train Acc: 98.33% | Val Loss: 1.12 | Val Acc: 62.40%\n",
            "Epoch  80 | Train Loss: 0.130 | Train Acc: 98.33% | Val Loss: 1.19 | Val Acc: 62.20%\n",
            "Epoch 100 | Train Loss: 0.158 | Train Acc: 98.33% | Val Loss: 1.10 | Val Acc: 64.60%\n",
            "GAT test accuracy: 68.10%\n"
          ]
        }
      ],
      "source": [
        "# Create the Vanilla GNN model\n",
        "gat = GAT(dataset.num_features, 16, dataset.num_classes)\n",
        "print(gat)\n",
        "\n",
        "# Train\n",
        "gat.fit(data, epochs=100)\n",
        "\n",
        "# Test\n",
        "acc = gat.test(data)\n",
        "print(f'GAT test accuracy: {acc*100:.2f}%')"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 58,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 1000
        },
        "id": "ciQljfVbRF8Y",
        "outputId": "f3891d4d-c993-4597-8a06-32ebb9c32ecf"
      },
      "outputs": [
        {
          "data": {
            "image/png": "iVBORw0KGgoAAAANSUhEUgAAAjcAAAGwCAYAAABVdURTAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjYuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/P9b71AAAACXBIWXMAAA9hAAAPYQGoP6dpAABbgklEQVR4nO3dd1gU59oG8Ht36V1BqhQ7FpqgBFv8DII1ajz2CBJjC9hQTyRRiSVil1hiixJTjMSaRGNFsWIDsSJWREVARIqggLvz/cFxkw2gLAILm/uXa6/LfeedmWcm7HIz886MSBAEAURERERqQqzqAoiIiIgqE8MNERERqRWGGyIiIlIrDDdERESkVhhuiIiISK0w3BAREZFaYbghIiIitaKh6gKqm0wmQ0pKCgwNDSESiVRdDhEREZWDIAjIzc2FtbU1xOI3H5v514WblJQU2NraqroMIiIiqoAHDx6gfv36b+zzrws3hoaGAIp3jpGRkYqrISIiovLIycmBra2t/Pf4m/zrws3rU1FGRkYMN0RERLVMeYaUcEAxERERqRWVh5vVq1fDwcEBOjo68PT0xLlz597YPzw8HM2aNYOuri5sbW0xefJkvHz5spqqJSIioppOpeEmMjISwcHBCA0NRVxcHFxcXODr64v09PRS+2/ZsgXTp09HaGgoEhISsHHjRkRGRuKLL76o5sqJiIiophIJgiCoauWenp5o06YNVq1aBaD4Mm1bW1uMHz8e06dPL9E/KCgICQkJiIqKkrdNmTIFZ8+excmTJ0tdR0FBAQoKCuTvXw9Iys7O5pgbIiKiWiInJwfGxsbl+v2tsiM3hYWFiI2Nhbe391/FiMXw9vZGTExMqfO0a9cOsbGx8lNXd+/exZ9//okePXqUuZ6wsDAYGxvLX7wMnIiISL2p7GqpjIwMSKVSWFhYKLRbWFjgxo0bpc4zdOhQZGRkoEOHDhAEAa9evcLYsWPfeFoqJCQEwcHB8vevj9wQERGRelL5gGJlREdHY/78+fj2228RFxeHnTt3Yu/evZg7d26Z82hra8sv++bl30RENYuDgwNEIlGJV2BgIJKSkkqdJhKJsG3btjKXWdY8ixcvlvfJzMzEsGHDYGRkBBMTE4wcORLPnz+XT09KSkKnTp2gr6+PTp06ISkpSWEdvXr1wo4dOyp9f1DlUFm4MTMzg0QiQVpamkJ7WloaLC0tS51n5syZGD58OD799FM4OTmhX79+mD9/PsLCwiCTyaqjbCIiqkTnz5/H48eP5a9Dhw4BAAYMGABbW1uFaY8fP8bs2bNhYGCA7t27l7nMf86zadMmiEQi9O/fX95n2LBhuHbtGg4dOoQ9e/bg+PHjGD16tHz6lClTYGNjg/j4eFhZWWHq1KnyaZGRkRCLxQrLo5pFZeFGS0sL7u7uCoODZTIZoqKi4OXlVeo8+fn5JZ4nIZFIABQ/c4KIiGqXevXqwdLSUv7as2cPGjVqhPfffx8SiURhmqWlJXbt2oWBAwfCwMCgzGX+c57ffvsN//d//4eGDRsCABISErB//35899138PT0RIcOHbBy5Ups3boVKSkp8j7+/v5o0qQJRowYgYSEBABAVlYWZsyYgdWrV1f9zqEKU+lpqeDgYGzYsAGbN29GQkICxo0bh7y8PAQEBAAA/Pz8EBISIu/fu3dvrFmzBlu3bsW9e/dw6NAhzJw5E71795aHHCIiqp0KCwvx008/4ZNPPin1LrSxsbGIj4/HyJEjy73MtLQ07N27V2GemJgYmJiYwMPDQ97m7e0NsViMs2fPAgBcXFxw+PBhyGQyHDx4EM7OzgCAadOmITAwkGM3aziVPn5h0KBBePLkCWbNmoXU1FS4urpi//798kHGycnJCkdqZsyYAZFIhBkzZuDRo0eoV68eevfuja+//lpVm0BERJVk9+7dyMrKwogRI0qdvnHjRjRv3hzt2rUr9zI3b94MQ0NDfPTRR/K21NRUmJubK/TT0NBA3bp1kZqaCgBYsmQJxowZAwcHBzg7O2PdunU4fvw44uPjsXDhQgwcOBAXLlyAj48PVqxYAS0tLeU3mKqMyp8tFRQUhKCgoFKnRUdHK7zX0NBAaGgoQkNDq6EyIiKqThs3bkT37t1hbW1dYtqLFy+wZcsWzJw5U6llbtq0CcOGDYOOjo5S89nY2GDPnj3y9wUFBfD19cXmzZsxb948GBoaIjExEd26dcO6deswfvx4pZZPVatWXS1FRETq6f79+zh8+DA+/fTTUqdv374d+fn58PPzK/cyT5w4gcTExBLLtLS0LHEn/FevXiEzM7PMC1rmz58PHx8fuLu7Izo6Gv3794empiY++uijEn+Ik+qp/MgNERFRREQEzM3N0bNnz1Knb9y4ER9++CHq1atX7mVu3LgR7u7ucHFxUWj38vJCVlYWYmNj4e7uDgA4cuQIZDIZPD09SywnISEBW7ZsQXx8PABAKpWiqKgIAFBUVASpVFrumqh68MgNERGplEwmQ0REBPz9/aGhUfJv7tu3b+P48eNlHtVxdHTErl27FNpycnKwbdu2Uudp3rw5unXrhlGjRuHcuXM4deoUgoKCMHjw4BKnxARBwOjRo7F8+XLo6+sDANq3b48NGzYgISEBP/zwA9q3b1/RTacqotJnS6mCMs+mICKiquEwfa/83y/uxSH911mwHrUOmnVtSvR9dmwz8q5Fw2bcRohEJf8mv7+wF0x7TIKB01+P88mN349nURtQP+gHiLX1S8wjfZGLzENr8eLOOQAi6DVrh7reYyDW0lXolxu/Dy/vXUS9fn/dCV+al4WMPxaj4PFN6DZwh2nPSRBr6iBpQelHnahyKPP7m+GGiIiq3d/DjbpguKlateLBmURE/1ZveuQAAHTu3LnEtLFjx75xmTt37oSPjw9MTU0hEonk40P+bv369ejcuTOMjIwgEomQlZWlML2goADDhw+HkZERmjZtisOHDytMX7x4Ma8KolqB4YaIqJq96ZEDr40aNUqhz6JFi964zLy8PHTo0AELFy4ss09+fj66detW5sOG169fj9jYWMTExGD06NEYOnSo/O7v9+7dw4YNG3hfsSrytsD7miAI6N69O0QiEXbv3v3GZaalpWHEiBGwtraGnp4eunXrhlu3bin0GTNmDBo1agRdXV3Uq1cPffr0UXh4dWZmJnr37g0DAwO4ubnh4sWLCvMHBgZi6dKl77bxVYBXSxERVbN/XvGzYMEC+SMHXtPT0yvzsuTSDB8+HABKPODx7yZNmgSg5D3EXktISMCHH36Ili1bomHDhpg2bRoyMjJQr149jBs3DgsXLuTp/Cpy/vx5hauurl69iq5duyoEXgAIDw8v9e7N/yQIAvr27QtNTU389ttvMDIywrJly+Dt7Y3r16/LB0e7u7tj2LBhsLOzQ2ZmJr766iv4+Pjg3r17kEgk+Prrr5Gbm4u4uDisWbMGo0aNwoULFwAAZ86cwdmzZ7FixYpK3BOVg0duiIhUqKxHDvz8888wMzNDq1atEBISgvz8/CqvxcXFBSdPnsSLFy9w4MABWFlZwczMDD///DN0dHTQr1+/Kq/h3+pNz9h6LT4+HkuXLsWmTZveurxbt27hzJkzWLNmDdq0aYNmzZphzZo1ePHiBX755Rd5v9GjR6NTp05wcHBA69atMW/ePDx48EAekhMSEjB48GA0bdoUo0ePlj9jq6ioCGPHjsXatWtr5OOPGG6IiFSotEcODB06FD/99BOOHj2KkJAQ/Pjjj/j444+rvJZPPvkELi4uaNGiBb7++mv8+uuvePbsGWbNmoWVK1dixowZaNy4MXx9ffHo0aMqr+ffqrTAm5+fj6FDh2L16tXlOqJXUFAAAAp3ZhaLxdDW1sbJkydLnScvLw8RERFo0KCB/NlZLi4uOHLkCF69eoUDBw7In7G1aNEidO7cWeH5XDUJww0RkQqV9siB0aNHw9fXF05OThg2bBh++OEH7Nq1C3fu3KnSWjQ1NbF69Wrcu3cP58+fR4cOHTBlyhRMmDABFy9exO7du3Hp0iW89957mDBhQpXW8m9WWuCdPHky2rVrhz59+pRrGY6OjrCzs0NISAiePXuGwsJCLFy4EA8fPsTjx48V+n777bcwMDCAgYEB9u3bh0OHDsmflTV9+nRoaGigUaNG2LVrFzZu3Ihbt25h8+bNmDlzJsaOHYuGDRti4MCByM7OrrR98K4YboiIVORtjxx47fVdc2/fvl0dZckdPXoU165dQ1BQEKKjo9GjRw/o6+tj4MCBfORAFfpn4P39999x5MgRhIeHl3sZmpqa2LlzJ27evIm6detCT08PR48eRffu3RUeSA0Aw4YNw8WLF3Hs2DE0bdoUAwcOxMuXLwEAxsbG2LJlC+7fv49jx46hRYsWGDNmDBYvXoyff/4Zd+/eRWJiIvT09DBnzpxK2wfviuGGiEhF3vbIgddeX9ZtZWVVDVUVe/nyJQIDA7Fu3TpIJBI+cqCalBZ4jxw5gjt37sDExAQaGhryuzj3798fnTt3LnNZ7u7uiI+PR1ZWFh4/foz9+/fj6dOnaNiwoUI/Y2NjNGnSBJ06dcL27dtx48aNEnd8fi0iIgImJibo06cPoqOj5YOWBwwYUKMCL6+WIiJSgbIeOXDnzh1s2bIFPXr0gKmpKS5fvozJkyejU6dO8vEOQPFph7CwMPkg38zMTCQnJyMlJQUAkJiYCADyAaoAkJqaitTUVPkRoCtXrsDQ0BB2dnaoW7euQn1z585Fjx494ObmBqD4kQPTpk1DQEAAVq1axUcOVJHSAu/06dNLHN1zcnLC8uXL0bt377cu09jYGEDxIOMLFy5g7ty5ZfYVBAGCIMjH7PzdkydPMGfOHPmYnZoceBluiIiq0es78764F4f05GT8mNkQW/92t95XOU+QsedXfDV/MWRFL6FhZAa9Jl4oaj1Y4a6+9xMTMeq745h8tnhsxPMrh/H0z3D59MGDBwMAjNsPgUmHYQCArJM/I/vUX1fKdOrUCQBKPLqg8EkSnuyKgNWIldj+v3UKgi4yTVvBtY0XNE1tYNZ7mrwe3pm3cpQVeP8eUP/Ozs4ODRo0kL//Z+Ddtm0b6tWrBzs7O1y5cgUTJ05E37594ePjAwC4e/cuIiMj4ePjg3r16uHhw4dYsGABdHV10aNHjxLrmzRpEqZMmQIbm+JHZLRv3x4//vgjfHx8sH79+hoVePn4BSKiasTHDhTjfviLQuB9wzO2/u7+wl6o1+9L6DX1Umj7e1DNufA7cs7thDQvCxKDOjBo2QXG7QdDJNEEALzKfYqn+1egMPUOZC+fQ6JvAm3bljBpNwSapvUV1vfibiyyTv4My+FL5M/3khW9xNO94XhxLxbaVk1h1nsaJPom77Qv3oTPlnoDhhsiUiX+Ui/G/fAX7ovy4bOliIiI6F+L4YaIiIjUCsMNERERqRWGGyIiIlIrDDdERESkVhhuiIiISK0w3BAREZFaYbghIiIitcJwQ0RERGqF4YaoGjg4OEAkEpV4BQYGAvjrCcympqYwMDBA//79kZaWVu7ljx07FiKRCOHh4aVOLygogKurK0QikfwJ0wCQlJSETp06QV9fH506dUJSUpLCfL169cKOHTuU3dwyVcV+GDFiRInldevW7a3rXbBggXx6de8HIqpaDDdE1eD8+fN4/Pix/HXo0CEAwIABAwAAkydPxh9//IFt27bh2LFjSElJwUcffVSuZe/atQtnzpyBtbV1mX3++9//ljr99UPw4uPjYWVlhalTp8qnRUZGQiwWo3///sps6htV1X7o1q2bwnJ/+eWXEn3mzJmj0Gf8+PHyadW9H4ioavGp4ETVoF69egrvFyxYgEaNGuH9999HdnY2Nm7ciC1btqBLly4AgIiICDRv3hxnzpzBe++9V+ZyHz16hPHjx+PAgQPo2bP0Z7ns27cPBw8exI4dO7Bv3z6FaQkJCVi2bBmaNGmCESNGyH+pZ2VlYcaMGThy5Mi7bHYJVbUftLW1S31q8t8ZGhqW2ae69wMRVS0euSGqZoWFhfjpp5/wySefQCQSITY2FkVFRfD29pb3cXR0hJ2dHWJiYspcjkwmw/DhwzFt2jS0bNmy1D5paWkYNWoUfvzxR+jp6ZWY7uLigsOHD0Mmk+HgwYNwdnYGAEybNg2BgYGwtbV9x60tW2XtBwCIjo6Gubk5mjVrhnHjxuHp06cl+ixYsACmpqZwc3PD4sWL8erVK/k0Ve4HIqp8DDdE1Wz37t3IysrCiBEjAACpqanQ0tKCiYmJQj8LCwukpqaWuZyFCxdCQ0MDEyZMKHW6IAgYMWIExo4dCw8Pj1L7LFmyBDdu3ICDgwNu3bqFJUuW4Pjx44iPj4efnx8GDhyIhg0bYuzYsSgsLKzQ9palsvZDt27d8MMPPyAqKgoLFy7EsWPH0L17d0ilUnmfCRMmYOvWrTh69CjGjBmD+fPn47///W+N2A9EVPl4Woqomm3cuBHdu3d/4xiZt4mNjcU333yDuLg4iESiUvusXLkSubm5CAkJKXM5NjY22LNnj/x9QUEBfH19sXnzZsybNw+GhoZITExEt27dsG7dOoVxKu+qMvYDAAwePFj+bycnJzg7O6NRo0aIjo7GBx98AAAIDg6W93F2doaWlhbGjBmDsLAwaGtrq3Q/EFHl45Ebomp0//59HD58GJ9++qm8zdLSEoWFhcjKylLom5aWVuYYkRMnTiA9PR12dnbQ0NCAhoYG7t+/jylTpsDBwQEAcOTIEcTExEBbWxsaGhpo3LgxAMDDwwP+/v6lLnf+/Pnw8fGBu7s7oqOj0b9/f2hqauKjjz5CdHT0O2//a5W1H0rTsGFDmJmZ4fbt22X28fT0xKtXr0pcFfVade0HIqoaPHJDVI0iIiJgbm6uMPjX3d0dmpqaiIqKkl+Rk5iYiOTkZHh5eZW6nOHDhyuMTQEAX19fDB8+HAEBAQCAFStWYN68efLpKSkp8PX1RWRkJDw9PUssMyEhAVu2bJFfKi6VSlFUVAQAKCoqUjjN864qaz+U5uHDh3j69CmsrKzK7BMfHw+xWAxzc/MS06pzPxBR1agRR25Wr14NBwcH6OjowNPTE+fOnSuzb+fOnUu9T0ZZV4oQ1RQymQwRERHw9/eHhsZff1cYGxtj5MiRCA4OxtGjRxEbG4uAgAB4eXkpXCHk6OiIXbt2AQBMTU3RqlUrhZempiYsLS3RrFkzAICdnZ3C9KZNmwIAGjVqhPr16yvUJggCRo8ejeXLl0NfXx8A0L59e2zYsAEJCQn44Ycf0L59+xq3H54/f45p06bhzJkzSEpKQlRUFPr06YPGjRvD19cXABATE4Pw8HBcunQJd+/exc8//4zJkyfj448/Rp06dVS2H4io6qj8yE1kZCSCg4Oxdu1aeHp6Ijw8HL6+vkhMTCz1r6qdO3cqDOh7+vQpXFxc5PfJIKqpDh8+jOTkZHzyySclpi1fvlx+L5XX4z2+/fZbhT6JiYnIzs6uktrWr18PCwsL9OrVS9721VdfYejQofD09ES3bt3kN9qrKIfpewEAL+7FIT05GT9mNsTW/7W9Juj7INP0Abx7fAhBWgSdBq1h2vUz+bwAcD8xEaO+O47JZ7UgKyrAk51RWPbtBshe5kFiUBe6Ddxg4jsZzUIPAwAKUm8j8+BaFE2fAUiLoGFsAf2W3RFdr5/CcgEgN34fXqbLEHRShKCTxdOkkvbIuHoaLV3dodvAHStTG2L1/+ZLWsA/qohqIpEgCIIqC/D09ESbNm2watUqAMV/1dna2mL8+PGYPn36W+cPDw/HrFmz8PjxY/lfWm+Sk5MDY2NjZGdnw8jI6J3rJ3qTf/7yVAcV/YXOfVGM+6EY98NfuC/KR5nf3yo9LVVYWIjY2FiFsQNisRje3t5vva/Faxs3bsTgwYPLDDYFBQXIyclReBEREZH6Umm4ycjIgFQqhYWFhUL72+5r8dq5c+dw9epVhSsu/iksLAzGxsbyF2/GRUREpN5qxIDiitq4cSOcnJzQtm3bMvuEhIQgOztb/nrw4EE1VkhERETVTaUDis3MzCCRSEo89bc897XIy8vD1q1bMWfOnDf209bWhra29jvXSkRERLWDSo/caGlpwd3dHVFRUfI2mUyGqKiot97XYtu2bSgoKMDHH39c1WUSERFRLaLyS8GDg4Ph7+8PDw8PtG3bFuHh4cjLy5PfiMzPzw82NjYICwtTmG/jxo3o27cvTE1NVVE2ERER1VAqDzeDBg3CkydPMGvWLKSmpsLV1RX79++XDzJOTk6GWKx4gCkxMREnT57EwYMHVVEyERER1WAqDzcAEBQUhKCgoFKnlfYcl2bNmkHFt+chIiKiGqpWXy1FRERE9E8MN0RERKRWGG6IiIhIrTDcEBERkVphuCEiIiK1wnBDREREaoXhhoiIiNQKww0RERGpFYYbIiIiUisMN0RERKRWGG6IiIhIrTDcEBERkVphuCEiIiK1wnBDREREaoXhhoiIiNQKww0RERGpFYYbIiIiUisMN0RERKRWGG6IiIhIrTDcEBERkVphuCEiIiK1wnBDREREaoXhhoiIiNQKww0RERGpFYYbqlKPHj3Cxx9/DFNTU+jq6sLJyQkXLlyQT9+5cyd8fHxgamoKkUiE+Pj4ty7z2rVr6N+/PxwcHCASiRAeHl6hdS9ZsgTm5uYwNzfH0qVLFeY9e/Ys3N3d8erVqwptNxERqY6Gqgsg9fXs2TO0b98e//d//4d9+/ahXr16uHXrFurUqSPvk5eXhw4dOmDgwIEYNWpUuZabn5+Phg0bYsCAAZg8eXKF1n358mXMmjULe/bsgSAI6NWrF3x8fODk5IRXr15h7NixWL9+PTQ0+BEhIqpt+M1NVWbhwoWwtbVFRESEvK1BgwYKfYYPHw4ASEpKKvdy27RpgzZt2gAApk+fXqF137hxA87OzujSpQsAwNnZGTdu3ICTkxMWL16MTp06yddBRES1C09LUZX5/fff4eHhgQEDBsDc3Bxubm7YsGFDjVi3k5MTbt68ieTkZNy/fx83b95Eq1atcOfOHURERGDevHnVUicREVU+hhuqMnfv3sWaNWvQpEkTHDhwAOPGjcOECROwefNmla+7efPmmD9/Prp27QofHx+EhYWhefPmGDNmDBYtWoQDBw6gVatWcHNzw/Hjx6u8XiIiqjw8LUVVRiaTwcPDA/PnzwcAuLm54erVq1i7di38/f1Vvu6xY8di7Nix8nk2b94MQ0NDeHl5oVmzZjh//jwePnyIwYMH4969e9DW1q7SmomIqHLwyA1VGSsrK7Ro0UKhrXnz5khOTq5x687IyMDs2bOxcuVKnD17Fk2bNkWTJk3wf//3fygqKsLNmzervGYiIqocDDdUZdq3b4/ExESFtps3b8Le3r7GrXvy5MmYPHky6tevD6lUiqKiIvm0V69eQSqVVmm9RERUeXhaiqrM5MmT0a5dO8yfPx8DBw7EuXPnsH79eqxfv17eJzMzE8nJyUhJSQEAeSCxtLSEpaUlAMDPzw82NjYICwsDABQWFuL69evyfz969Ajx8fEwMDBA48aNy73u1w4dOoSbN2/Kx+O0adMGN27cwL59+/DgwQNIJBI0a9asivYSERFVNpEgCIKqi6hOOTk5MDY2RnZ2NoyMjFRdjtpymL4XAJB/+xyyjm1G0bMUaBhbwKhNXxi6dpP3e37lMJ7+GV5ifuP2Q2DSYRgAIHXLdGgYW8CsZ/E9bV5lp+HR2pEl5tG2bQXLoQvk79+2bgCQFRXg8fcTUO/Dz6Fl0VDennvpALJO/AiRRBN1fT6DXqM2SFrQs8L7QZ1UZD8A3BevcT8U4374C/dF+Sjz+1vlR25Wr16NxYsXIzU1FS4uLli5ciXatm1bZv+srCx8+eWX2LlzJzIzM2Fvb4/w8HD06NGjGqum8tJr3BZ6jcv+/2ng5A0DJ+83LuPvgQUANIwtYP/5nndeNwCINbVhM2pdiXZDF18Yuvi+dR1ERFTzqDTcREZGIjg4GGvXroWnpyfCw8Ph6+uLxMREmJubl+hfWFiIrl27wtzcHNu3b4eNjQ3u378PExOT6i+eiIiIaiSVhptly5Zh1KhRCAgIAACsXbsWe/fuxaZNm0q98+ymTZuQmZmJ06dPQ1NTEwDg4OBQnSUTERFRDaeyq6UKCwsRGxsLb++/TkmIxWJ4e3sjJiam1Hl+//13eHl5ITAwEBYWFmjVqhXmz5//xitZCgoKkJOTo/AiIiIi9aWycJORkQGpVAoLCwuFdgsLC6SmppY6z927d7F9+3ZIpVL8+eefmDlzJpYuXfrGW+WHhYXB2NhY/rK1ta3U7SAiIqKapVbd50Ymk8Hc3Bzr16+Hu7s7Bg0ahC+//BJr164tc56QkBBkZ2fLXw8ePKjGiomIiKi6qWzMjZmZGSQSCdLS0hTa09LS5Pc3+ScrKytoampCIpHI25o3b47U1FQUFhZCS0urxDza2tq8bT4REdG/iMqO3GhpacHd3R1RUVHyNplMhqioKHh5eZU6T/v27XH79m3IZDJ5282bN2FlZVVqsCEiIqJ/H5WelgoODsaGDRuwefNmJCQkYNy4ccjLy5NfPeXn54eQkBB5/3HjxiEzMxMTJ07EzZs3sXfvXsyfPx+BgYGq2gQiIiKqYVR6KfigQYPw5MkTzJo1C6mpqXB1dcX+/fvlg4yTk5MhFv+Vv2xtbXHgwAFMnjwZzs7OsLGxwcSJE/H555+rahOIiIiohlH5HYqDgoIQFBRU6rTo6OgSbV5eXjhz5kwVV0VERES1Va26WoqIiIjobRhuiIiISK0w3BAREZFaYbghIiIitcJwQ0RERGqF4YaIiIjUCsMNERERqRWGGyIiIlIrDDdERESkVhhuiIiISK0w3BAREZFaYbghIiIitcJwQ0RERGqF4YaIiIjUCsMNERERqRWGGyIiIlIrDDdERESkVhhuiIiISK0w3BAREZFaYbghIiIitcJwQ0RERGqF4YaIiIjUCsMNERERqRWGGyIiIlIrDDdERESkVhhuiIiISK0w3BAREZFaYbghIiIitcJwQ0RERGqlQuHm1atXOHz4MNatW4fc3FwAQEpKCp4/f16pxdVWX331FUQikcLL0dFRPv3ly5cIDAyEqakpDAwM0L9/f6Slpb1xmYIgYNasWbCysoKuri68vb1x69YthT6ZmZkYNmwYjIyMYGJigpEjRyr8P0lKSkKnTp2gr6+PTp06ISkpSWH+Xr16YceOHe++A4iIiFRI6XBz//59ODk5oU+fPggMDMSTJ08AAAsXLsTUqVMrvcDaqmXLlnj8+LH8dfLkSfm0yZMn448//sC2bdtw7NgxpKSk4KOPPnrj8hYtWoQVK1Zg7dq1OHv2LPT19eHr64uXL1/K+wwbNgzXrl3DoUOHsGfPHhw/fhyjR4+WT58yZQpsbGwQHx8PKysrhf9fkZGREIvF6N+/fyXuBSIiouqndLiZOHEiPDw88OzZM+jq6srb+/Xrh6ioqEotrjbT0NCApaWl/GVmZgYAyM7OxsaNG7Fs2TJ06dIF7u7uiIiIwOnTp3HmzJlSlyUIAsLDwzFjxgz06dMHzs7O+OGHH5CSkoLdu3cDABISErB//35899138PT0RIcOHbBy5Ups3boVKSkp8j7+/v5o0qQJRowYgYSEBABAVlYWZsyYgdWrV1f9jiEiIqpiSoebEydOYMaMGdDS0lJod3BwwKNHjyqtsNru1q1bsLa2RsOGDTFs2DAkJycDAGJjY1FUVARvb295X0dHR9jZ2SEmJqbUZd27dw+pqakK8xgbG8PT01M+T0xMDExMTODh4SHv4+3tDbFYjLNnzwIAXFxccPjwYchkMhw8eBDOzs4AgGnTpiEwMBC2traVuxOIiIhUQOlwI5PJIJVKS7Q/fPgQhoaGlVJUbefp6Ynvv/8e+/fvx5o1a3Dv3j107NgRubm5SE1NhZaWFkxMTBTmsbCwQGpqaqnLe91uYWFR5jypqakwNzdXmK6hoYG6devK+yxZsgQ3btyAg4MDbt26hSVLluD48eOIj4+Hn58fBg4ciIYNG2Ls2LEoLCysjF1BRERU7TSUncHHxwfh4eFYv349AEAkEuH58+cIDQ1Fjx49Kr3A2qh79+7yfzs7O8PT0xP29vb49ddfFU7lVTcbGxvs2bNH/r6goAC+vr7YvHkz5s2bB0NDQyQmJqJbt25Yt24dxo8fr7JaiYiIKkrpIzdLlizBqVOn0KJFC7x8+RJDhw6Vn5JauHBhhYpYvXo1HBwcoKOjA09PT5w7d67Mvt9//32JK5F0dHQqtN7qYmJigqZNm+L27duwtLREYWEhsrKyFPqkpaXB0tKy1Plft//ziqq/z2NpaYn09HSF6a9evUJmZmaZy50/fz58fHzg7u6O6Oho9O/fH5qamvjoo48QHR1dgS0lIiJSPaXDja2tLS5duoQvv/wSkydPhpubGxYsWICLFy+WOC1SHpGRkQgODkZoaCji4uLg4uICX1/fEr+o/87IyEjhSqT79+8rvd7q9Pz5c9y5cwdWVlZwd3eHpqamwuDrxMREJCcnw8vLq9T5GzRoAEtLS4V5cnJycPbsWfk8Xl5eyMrKQmxsrLzPkSNHIJPJ4OnpWWKZCQkJ2LJlC+bOnQsAkEqlKCoqAgAUFRWVeuqRiIioNlDqtFRRUREcHR2xZ88eDBs2DMOGDXvnApYtW4ZRo0YhICAAALB27Vrs3bsXmzZtwvTp00udRyQSlXk04p8KCgpQUFAgf5+Tk/PONb/N1KlT0bt3b9jb2yMlJQWhoaGQSCQYMmQIjI2NMXLkSAQHB6Nu3bowMjLC+PHj4eXlhffee0++DEdHR4SFhaFfv34QiUSYNGkS5s2bhyZNmqBBgwaYOXMmrK2t0bdvXwBA8+bN0a1bN4waNQpr165FUVERgoKCMHjwYFhbWyvUJwgCRo8ejeXLl0NfXx8A0L59e2zYsAFNmzbFDz/8gCFDhlT5fiIiIqoKSoUbTU1NhfuqvKvCwkLExsYiJCRE3iYWi+Ht7V3mlUNA8ZEQe3t7yGQytG7dGvPnz0fLli1L7RsWFobZs2dXWs1v4zB9L57sPYfwdRGQvsiBRNcY2vVbwOSjMLRZWny6TdD3QabpA3j3+BCCtAg6DVrDtOtncJi+V76c+4mJGPXdcUw+W3xVmiC0hNCiG/oN9YfsZR506rdAXZ/pcPzqr6M50qb+yLy/Fu917AxABL1m7XDS8iOF5QJAbvw+vEyXIeikCEEni6dJJe2RcfU0Wrq6Q7eBO1amNsTq/82XtKBnFe4xIiKiyqX0gOLAwEAsXLgQ3333HTQ0lJ5dQUZGBqRSaalXAd24caPUeZo1a4ZNmzbB2dkZ2dnZWLJkCdq1a4dr166hfv36JfqHhIQgODhY/j4nJ6fKL3mu1+fzN04XaWjB1GccTH3GldnH/vM9Cu9FIhFMOn4Mk44flzmPRNcQ9T6c9tb6DF27w9C1u0KbRN8EFoO/fuu8RERENZ3S6eT8+fOIiorCwYMH4eTkJD+t8drOnTsrrbjSeHl5KYxNadeuHZo3b45169bJx4/8nba2NrS1tau0JiIiIqo5lA43JiYmlXaLfjMzM0gkkjdeBfQ2mpqacHNzw+3btyulJiIiIqrdlA43ERERlbZyLS0tuLu7IyoqSj4wViaTISoqCkFBQeVahlQqxZUrV3iPHSIiIgJQgXDz2pMnT5CYmAigeBxMvXr1KrSc4OBg+Pv7w8PDA23btkV4eDjy8vLkV0/5+fnBxsYGYWFhAIA5c+bgvffeQ+PGjZGVlYXFixfj/v37+PTTTyu6KURERKRGlA43eXl5GD9+PH744QfIZDIAgEQigZ+fH1auXAk9PT2lljdo0CA8efIEs2bNQmpqKlxdXbF//375IOPk5GSIxX/djufZs2cYNWoUUlNTUadOHbi7u+P06dNo0aKFsptCREREakjpcBMcHIxjx47hjz/+QPv27QEAJ0+exIQJEzBlyhSsWbNG6SKCgoLKPA31zzvlLl++HMuXL1d6HURERPTvoHS42bFjB7Zv347OnTvL23r06AFdXV0MHDiwQuGGiIiIqLIo/fiF/Pz8EvelAQBzc3Pk5+dXSlFEREREFaV0uPHy8kJoaKjCnYpfvHiB2bNnl/lsJCIiIqLqovRpqW+++Qa+vr6oX78+XFxcAACXLl2Cjo4ODhw4UOkFEhERESlD6XDTqlUr3Lp1Cz///LP8EQlDhgzBsGHDoKurW+kFEhERESmjQve50dPTw6hRoyq7FiIiIqJ3pvSYm7CwMGzatKlE+6ZNm7Bw4cJKKYqIiIioopQON+vWrYOjo2OJ9pYtW2Lt2rWVUhQRERFRRSkdblJTU2FlZVWivV69enj8+HGlFEVERERUUUqHG1tbW5w6dapE+6lTp2BtbV0pRRERERFVlNIDikeNGoVJkyahqKgIXbp0AQBERUXhv//9L6ZMmVLpBRIREREpQ+lwM23aNDx9+hSfffYZCgsLAQA6Ojr4/PPPERISUukFEhERESlD6XAjEomwcOFCzJw5EwkJCdDV1UWTJk2gra1dFfURERERKUXpMTevGRgYoE2bNrCzs8O+ffuQkJBQmXURERERVYjS4WbgwIFYtWoVgOJnSnl4eGDgwIFwdnbGjh07Kr1AIiIiImUoHW6OHz+Ojh07AgB27doFQRCQlZWFFStWYN68eZVeIBEREZEylA432dnZqFu3LgBg//796N+/P/T09NCzZ0/cunWr0gskIiIiUkaF7nMTExODvLw87N+/Hz4+PgCAZ8+eQUdHp9ILJCIiIlKG0ldLTZo0CcOGDYOBgQHs7e3RuXNnAMWnq5ycnCq7PiIiIiKlKB1uPvvsM3h6eiI5ORldu3aFWFx88Kdhw4Ycc0NEREQqp3S4AQB3d3e4u7srtPXs2bNSCiIiIiJ6FxW+zw0RERFRTcRwQ0RERGqF4YaIiIjUCsMNERERqRWlw42DgwPmzJmD5OTkqqiHiIiI6J0oHW4mTZqEnTt3omHDhujatSu2bt2KgoKCqqiNiIiISGkVCjfx8fE4d+4cmjdvjvHjx8PKygpBQUGIi4urihqJiIiIyq3CY25at26NFStWICUlBaGhofjuu+/Qpk0buLq6YtOmTRAEoTLrJCIiIiqXCt3EDwCKioqwa9cuRERE4NChQ3jvvfcwcuRIPHz4EF988QUOHz6MLVu2VGatRERERG+ldLiJi4tDREQEfvnlF4jFYvj5+WH58uVwdHSU9+nXrx/atGlTqYUSERERlYfS4aZNmzbo2rUr1qxZg759+0JTU7NEnwYNGmDw4MGVUiARERGRMpQON3fv3oW9vf0b++jr6yMiIqLCRRERERFVlNIDitPT03H27NkS7WfPnsWFCxcqpSgiIiKiilI63AQGBuLBgwcl2h89eoTAwMAKFbF69Wo4ODhAR0cHnp6eOHfuXLnm27p1K0QiEfr27Vuh9RIREZH6UTrcXL9+Ha1bty7R7ubmhuvXrytdQGRkJIKDgxEaGoq4uDi4uLjA19cX6enpb5wvKSkJU6dORceOHZVeJxEREakvpcONtrY20tLSSrQ/fvwYGhrKX1m+bNkyjBo1CgEBAWjRogXWrl0LPT09bNq0qcx5pFIphg0bhtmzZ6Nhw4ZKr5OIiIjUl9LhxsfHByEhIcjOzpa3ZWVl4YsvvkDXrl2VWlZhYSFiY2Ph7e39V0FiMby9vRETE1PmfHPmzIG5uTlGjhz51nUUFBQgJydH4UVERETqS+lDLUuWLEGnTp1gb28PNzc3AEB8fDwsLCzw448/KrWsjIwMSKVSWFhYKLRbWFjgxo0bpc5z8uRJbNy4EfHx8eVaR1hYGGbPnq1UXURERFR7KX3kxsbGBpcvX8aiRYvQokULuLu745tvvsGVK1dga2tbFTXK5ebmYvjw4diwYQPMzMzKNc/ro0yvX6UNhiYiIiL1UaHHL+jr62P06NHvvHIzMzNIJJISY3jS0tJgaWlZov+dO3eQlJSE3r17y9tkMhkAQENDA4mJiWjUqJHCPNra2tDW1n7nWomIiKh2qPCzpa5fv47k5GQUFhYqtH/44YflXoaWlhbc3d0RFRUlv5xbJpMhKioKQUFBJfo7OjriypUrCm0zZsxAbm4uvvnmmyo/ckREREQ1X4XuUNyvXz9cuXIFIpFI/vRvkUgEoPhKJmUEBwfD398fHh4eaNu2LcLDw5GXl4eAgAAAgJ+fH2xsbBAWFgYdHR20atVKYX4TExMAKNFORERE/05Kj7mZOHEiGjRogPT0dOjp6eHatWs4fvw4PDw8EB0drXQBgwYNwpIlSzBr1iy4uroiPj4e+/fvlw8yTk5OxuPHj5VeLhEREf07KX3kJiYmBkeOHIGZmRnEYjHEYjE6dOiAsLAwTJgwARcvXlS6iKCgoFJPQwF4a2D6/vvvlV4fERERqS+lj9xIpVIYGhoCKB4QnJKSAgCwt7dHYmJi5VZHREREpCSlj9y0atUKly5dQoMGDeDp6YlFixZBS0sL69ev592CiYiISOWUDjczZsxAXl4egOI7Bffq1QsdO3aEqakpIiMjK71AIiIiImUoHW58fX3l/27cuDFu3LiBzMxM1KlTR37FFBEREZGqKDXmpqioCBoaGrh69apCe926dRlsiIiIqEZQKtxoamrCzs5O6XvZEBEREVUXpa+W+vLLL/HFF18gMzOzKuohIiIieidKj7lZtWoVbt++DWtra9jb20NfX19helxcXKUVR0RERKQspcPN62dAEREREdVESoeb0NDQqqiDiIiIqFIoPeaGiIiIqCZT+siNWCx+42XfvJKKiIiIVEnpcLNr1y6F90VFRbh48SI2b96M2bNnV1phRERERBWhdLjp06dPibb//Oc/aNmyJSIjIzFy5MhKKYyIiIioIiptzM17772HqKioylocERERUYVUSrh58eIFVqxYARsbm8pYHBEREVGFKX1a6p8PyBQEAbm5udDT08NPP/1UqcURERERKUvpcLN8+XKFcCMWi1GvXj14enqiTp06lVocERERkbKUDjcjRoyogjKIiIiIKofSY24iIiKwbdu2Eu3btm3D5s2bK6UoIiIioopSOtyEhYXBzMysRLu5uTnmz59fKUURERERVZTS4SY5ORkNGjQo0W5vb4/k5ORKKYqIiIioopQON+bm5rh8+XKJ9kuXLsHU1LRSiiIiIiKqKKXDzZAhQzBhwgQcPXoUUqkUUqkUR44cwcSJEzF48OCqqJGIiIio3JS+Wmru3LlISkrCBx98AA2N4tllMhn8/Pw45oaIiIhUTulwo6WlhcjISMybNw/x8fHQ1dWFk5MT7O3tq6I+IiIiIqUoHW5ea9KkCZo0aVKZtRARERG9M6XH3PTv3x8LFy4s0b5o0SIMGDCgUooiIiIiqiilw83x48fRo0ePEu3du3fH8ePHK6UoIiIioopSOtw8f/4cWlpaJdo1NTWRk5NTKUURERERVZTS4cbJyQmRkZEl2rdu3YoWLVpUSlFEREREFaX0gOKZM2fio48+wp07d9ClSxcAQFRUFH755ZdSnzlFREREVJ2UDje9e/fG7t27MX/+fGzfvh26urpwdnbG4cOH8f7771dFjURERETlVqFLwXv27ImePXuWaL969SpatWr1zkURERERVZTSY27+KTc3F+vXr0fbtm3h4uJSoWWsXr0aDg4O0NHRgaenJ86dO1dm3507d8LDwwMmJibQ19eHq6srfvzxx4qWT0RERGqmwuHm+PHj8PPzg5WVFZYsWYIuXbrgzJkzSi8nMjISwcHBCA0NRVxcHFxcXODr64v09PRS+9etWxdffvklYmJicPnyZQQEBCAgIAAHDhyo6KYQERGRGlEq3KSmpmLBggVo0qQJBgwYAGNjYxQUFGD37t1YsGAB2rRpo3QBy5Ytw6hRoxAQEIAWLVpg7dq10NPTw6ZNm0rt37lzZ/Tr1w/NmzdHo0aNMHHiRDg7O+PkyZNKr5uIiIjUT7nDTe/evdGsWTNcvnwZ4eHhSElJwcqVK99p5YWFhYiNjYW3t/dfBYnF8Pb2RkxMzFvnFwQBUVFRSExMRKdOnUrtU1BQgJycHIUXERERqa9yDyjet28fJkyYgHHjxlXaM6UyMjIglUphYWGh0G5hYYEbN26UOV92djZsbGxQUFAAiUSCb7/9Fl27di21b1hYGGbPnl0p9RIREVHNV+4jNydPnkRubi7c3d3h6emJVatWISMjoyprK5OhoSHi4+Nx/vx5fP311wgODkZ0dHSpfUNCQpCdnS1/PXjwoHqLJSIiompV7nDz3nvvYcOGDXj8+DHGjBmDrVu3wtraGjKZDIcOHUJubq7SKzczM4NEIkFaWppCe1paGiwtLcsuWixG48aN4erqiilTpuA///kPwsLCSu2rra0NIyMjhRcRERGpL6WvltLX18cnn3yCkydP4sqVK5gyZQoWLFgAc3NzfPjhh0otS0tLC+7u7oiKipK3yWQyREVFwcvLq9zLkclkKCgoUGrdREREpJ7e6T43zZo1w6JFi/Dw4UP88ssvFVpGcHAwNmzYgM2bNyMhIQHjxo1DXl4eAgICAAB+fn4ICQmR9w8LC8OhQ4dw9+5dJCQkYOnSpfjxxx/x8ccfv8umEBERkZqo0B2K/0kikaBv377o27ev0vMOGjQIT548waxZs5CamgpXV1fs379fPsg4OTkZYvFfGSwvLw+fffYZHj58CF1dXTg6OuKnn37CoEGDKmNTiIiIqJarlHDzroKCghAUFFTqtH8OFJ43bx7mzZtXDVURERFRbfTOj18gIiIiqkkYboiIiEitMNwQERGRWmG4ISIiIrXCcENERERqheGGiIiI1ArDDREREakVhhsiIiJSKww3REREpFYYboiIiEitMNwQERGRWmG4ISIiIrXCcENERERqheGGiIiI1ArDDREREakVhhsiIiJSKww3REREpFYYboiIiEitMNwQERGRWmG4ISIiIrXCcENERERqheGGiIiI1ArDDREREakVhhsiIiJSKww3REREpFYYboiIiEitMNwQERGRWmG4ISIiIrXCcENERERqheGGiIiI1ArDDREREakVhhsiIiJSKww3REREpFYYboiIiEit1Ihws3r1ajg4OEBHRweenp44d+5cmX03bNiAjh07ok6dOqhTpw68vb3f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",
            "text/plain": [
              "<Figure size 640x480 with 1 Axes>"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "# Get model's classifications\n",
        "out = gat(data.x, data.edge_index)\n",
        "\n",
        "# Calculate the degree of each node\n",
        "degrees = degree(data.edge_index[0]).numpy()\n",
        "\n",
        "# Store accuracy scores and sample sizes\n",
        "accuracies = []\n",
        "sizes = []\n",
        "\n",
        "# Accuracy for degrees between 0 and 5\n",
        "for i in range(0, 6):\n",
        "    mask = np.where(degrees == i)[0]\n",
        "    accuracies.append(accuracy(out.argmax(dim=1)[mask], data.y[mask]))\n",
        "    sizes.append(len(mask))\n",
        "\n",
        "# Accuracy for degrees > 5\n",
        "mask = np.where(degrees > 5)[0]\n",
        "accuracies.append(accuracy(out.argmax(dim=1)[mask], data.y[mask]))\n",
        "sizes.append(len(mask))\n",
        "\n",
        "# Bar plot\n",
        "fig, ax = plt.subplots()\n",
        "ax.set_xlabel('Node degree')\n",
        "ax.set_ylabel('Accuracy score')\n",
        "plt.bar(['0','1','2','3','4','5','6+'], accuracies)\n",
        "for i in range(0, 7):\n",
        "    plt.text(i, accuracies[i], f'{accuracies[i]*100:.2f}%', ha='center', color='black')\n",
        "for i in range(0, 7):\n",
        "    plt.text(i, accuracies[i]//2, sizes[i], ha='center', color='white')"
      ]
    }
  ],
  "metadata": {
    "colab": {
      "provenance": []
    },
    "kernelspec": {
      "display_name": "book",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.8.15 (default, Nov 24 2022, 14:38:14) [MSC v.1916 64 bit (AMD64)]"
    },
    "vscode": {
      "interpreter": {
        "hash": "3556630122da5213751af4465d61fcf5a52cd22515d400aee51118aaa1721248"
      }
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
